﻿using System;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

namespace IOP.Net
{
    /// <summary>
    /// 监听者
    /// </summary>
    public class SocketMonitor : ISocketMonitor
    {
        /// <summary>
        /// 标识符
        /// </summary>
        public string Identifier { get; set; } = "";
        /// <summary>
        /// 全局唯一标识符
        /// </summary>
        public Guid Guid { get; set; } = Guid.NewGuid();
        /// <summary>
        /// 持久连接时间
        /// </summary>
        public int KeepAlive { get; private set; }

        private bool _EnableKeepAlive = false;
        /// <summary>
        /// 是否启用保持连接
        /// </summary>
        public bool EnableKeepAlive 
        {
            get { return Volatile.Read(ref _EnableKeepAlive); }
        }

        private bool _IsDispose = false;
        /// <summary>
        /// 是否释放资源
        /// </summary>
        public bool IsDispose
        { 
            get { return Volatile.Read(ref _IsDispose); }
        }

        private bool _IsConnected = false;
        /// <summary>
        /// 是否连接
        /// </summary>
        public bool IsConnected 
        {
            get { return Volatile.Read(ref _IsConnected); }
        }

        /// <summary>
        /// 保持连接超时事件
        /// </summary>
        public event Action<ISocketMonitor> KeepAliveTimeout;
        /// <summary>
        /// 连接的Socket
        /// </summary>
        public Socket ConnectedSocket { get; set; }

        /// <summary>
        /// 上一次报文的接收时间
        /// </summary>
        private DateTime _LastMessageTime = DateTime.Now;
        /// <summary>
        /// 区间
        /// </summary>
        private TimeSpan _Section { get => DateTime.Now.Subtract(_LastMessageTime); }
        /// <summary>
        /// 信号控制器
        /// </summary>
        private readonly AutoResetEvent _ResetEvent = new AutoResetEvent(false);
        /// <summary>
        /// 线程取消令牌
        /// </summary>
        private CancellationTokenSource _CancelToken { get; set; } = new CancellationTokenSource();

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="socket">嵌套字</param>
        /// <param name="enableKeepAlive">是否启动保持连接检查</param>
        /// <param name="keepAlive">检查保持连接周期</param>
        public SocketMonitor(Socket socket, bool enableKeepAlive = false, int keepAlive = 120)
        {
            ConnectedSocket = socket;
            KeepAlive = keepAlive;
            _EnableKeepAlive = enableKeepAlive;
            if (socket.Connected) Volatile.Write(ref _IsConnected, true);
            if(_EnableKeepAlive) RunCheck();
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public Task SendAsync(ReadOnlyMemory<byte> data)
        {
            ConnectedSocket.Send(data.Span);
            return Task.CompletedTask;
        }

        /// <summary>
        /// 关闭连接
        /// </summary>
        /// <returns></returns>
        public Task CloseAsync()
        {
            ConnectedSocket.Shutdown(SocketShutdown.Both);
            ConnectedSocket.Close();
            Volatile.Write(ref _IsConnected, false);
            _CancelToken.Cancel();
            _ResetEvent.Set();
            return Task.CompletedTask;
        }

        /// <summary>
        /// 更新上一次报文接收时间
        /// </summary>
        /// <param name="time"></param>
        public void UpdateLastMessageTime(DateTime time)
        {
            _LastMessageTime = time;
        }
        /// <summary>
        /// 允许保持连接并启动检查函数
        /// </summary>
        /// <param name="keepAlive"></param>
        public void EnableKeepAliveCheckAndStart(int keepAlive = 120)
        {
            if (EnableKeepAlive) return;
            KeepAlive = keepAlive;
            Volatile.Write(ref _EnableKeepAlive, true);
            UpdateLastMessageTime(DateTime.Now);
            RunCheck();
        }
        /// <summary>
        /// 取消保持连接检查函数
        /// </summary>
        public void DisableKeepAliveCheck()
        {
            Volatile.Write(ref _EnableKeepAlive, false);
            _CancelToken.Cancel();
            _ResetEvent.Set();
            _CancelToken = new CancellationTokenSource();
        }

        /// <summary>
        /// 重置
        /// </summary>
        public void Restart()
        {
            if (!EnableKeepAlive) return;
            UpdateLastMessageTime(DateTime.Now);
            _ResetEvent.Set();
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            KeepAliveTimeout = null;
            Volatile.Write(ref _IsDispose, true);
            Volatile.Write(ref _IsConnected, false);
            _CancelToken.Cancel();
            _ResetEvent.Set();
            _ResetEvent.Dispose();
            _CancelToken.Dispose();
            ConnectedSocket.Shutdown(SocketShutdown.Both);
            ConnectedSocket.Close();
            ConnectedSocket.Dispose();
        }

        /// <summary>
        /// 执行检查
        /// </summary>
        private void RunCheck()
        {
            Task.Factory.StartNew(() =>
            {
                while (!_CancelToken.IsCancellationRequested)
                {
                    if (IsDispose) break;
                    if (_Section.TotalSeconds > KeepAlive)
                    {
                        KeepAliveTimeout?.Invoke(this);
                        break;
                    }
                    else
                    {                        
                        _ResetEvent.WaitOne((int)(KeepAlive * 1000 - _Section.TotalMilliseconds));
                    }
                }
            }, _CancelToken.Token);
        }
    }
}
